package com.wiscamp.ninechapters.problems.infrastructure.repositories;

import com.wiscamp.ninechapters.problems.domain.models.collections.CollectionPart;
import com.wiscamp.ninechapters.problems.domain.models.collections.CollectionSection;
import com.wiscamp.ninechapters.problems.domain.repositories.CollectionPartRepository;
import com.wiscamp.ninechapters.problems.infrastructure.converters.ProblemCollectionConverter;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.collections.CollectionPartMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.collections.CollectionSectionMapper;
import com.wiscamp.ninechapters.problems.infrastructure.po.collections.CollectionPartPO;
import com.wiscamp.ninechapters.problems.infrastructure.po.collections.CollectionSectionPO;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;

@Repository
public class CollectionPartRepositoryImpl extends BaseRepository implements CollectionPartRepository {

    private final CollectionPartMapper collectionPartMapper;

    private final CollectionSectionMapper collectionSectionMapper;

    public CollectionPartRepositoryImpl(CollectionPartMapper collectionPartMapper, CollectionSectionMapper collectionSectionMapper) {
        this.collectionPartMapper = collectionPartMapper;
        this.collectionSectionMapper = collectionSectionMapper;
    }

    //region part
    private void loadDetails(CollectionPart part) {
        if (Objects.nonNull(part)) {
            var sections = getCollectionSections(part.getPartId());
            part.setSections(sections);
        }
    }

    private CollectionPartPO getPartPO(@NotNull long partId) {
        return PersistenceUtils.findById(partId, collectionPartMapper::findById);
    }

    /**
     * Gets a paper part
     *
     * @param partId
     * @return
     */
    @HandleDataException
    public CollectionPart getCollectionPart(@NotNull long partId) {
        var part = PersistenceUtils.findEntityById(partId, collectionPartMapper::findById,
                ProblemCollectionConverter.Instance::toCollectionPart);
        loadDetails(part);
        return part;
    }

    /**
     * Gets a paper part of a problem collection
     *
     * @param collectionId
     * @return
     */
    @HandleDataException
    public Collection<CollectionPart> getCollectionParts(@NotNull long collectionId) {
        var parts = PersistenceUtils.getEntitiesById(collectionId, collectionPartMapper::findListByCollection,
                ProblemCollectionConverter.Instance::toCollectionPart);
        for (var part : parts) {
            loadDetails(part);
        }
        return parts;
    }

    /**
     * Creates a paper part
     *
     * @param part
     * @return
     */
    @HandleDataException
    public CollectionPart saveCollectionPart(@NotNull CollectionPart part) {
        var id = part.getPartId();
        if (part.getPartId() <= 0) {
            var newPart = PersistenceUtils.createEntity(part, collectionPartMapper::saveAndFlush,
                    ProblemCollectionConverter.Instance::toCollectionPartPO, ProblemCollectionConverter.Instance::toCollectionPart);
            id = newPart.getPartId();
        } else {
            var existedPO = getPartPO(part.getPartId());
            if (existedPO == null) existedPO = new CollectionPartPO();
            PersistenceUtils.updateEntity(part, existedPO, collectionPartMapper::saveAndFlush,
                    ProblemCollectionConverter.Instance::toCollectionPartPO, ProblemCollectionConverter.Instance::toCollectionPart);
        }

        return getCollectionPart(id);
    }

    /**
     * Deletes a paper part
     *
     * @param partId
     * @return
     */
    @HandleDataException
    public boolean deleteCollectionPart(@Min(0) long partId) {
        return PersistenceUtils.delete(partId, collectionPartMapper::deleteById);
    }
    // endregion

    // region section
    public CollectionSectionPO getSectionPO(@Min(0) long sectionId) {
        return PersistenceUtils.findById(sectionId, collectionSectionMapper::findById);
    }

    /**
     * Gets a paper section
     *
     * @param sectionId
     * @return
     */
    @HandleDataException
    public CollectionSection getCollectionSection(@Min(0) long sectionId) {
        return PersistenceUtils.findEntityById(sectionId, collectionSectionMapper::findById,
                ProblemCollectionConverter.Instance::toCollectionSection);
    }

    /**
     * Gets paper sections
     *
     * @param partId
     * @return
     */
    @HandleDataException
    public Collection<CollectionSection> getCollectionSections(@Min(0) long partId) {
        return PersistenceUtils.getEntitiesById(partId, collectionSectionMapper::findListByPart,
                ProblemCollectionConverter.Instance::toCollectionSection);
    }

    /**
     * Creates a paper section
     *
     * @param section
     * @return
     */
    @HandleDataException
    public CollectionSection saveCollectionSection(@NotNull CollectionSection section) {
        var id = section.getSectionId();
        if (section.getSectionId() <= 0) {
            var newSection = PersistenceUtils.createEntity(section, collectionSectionMapper::saveAndFlush,
                    ProblemCollectionConverter.Instance::toCollectionSectionPO, ProblemCollectionConverter.Instance::toCollectionSection);
            id = newSection.getSectionId();
        } else {
            var existedPO = getSectionPO(section.getSectionId());
            if (existedPO == null) existedPO = new CollectionSectionPO();
            PersistenceUtils.updateEntity(section, existedPO, collectionSectionMapper::saveAndFlush,
                    ProblemCollectionConverter.Instance::toCollectionSectionPO, ProblemCollectionConverter.Instance::toCollectionSection);
        }

        return getCollectionSection(id);
    }

    /**
     * Deletes a paper section
     *
     * @param sectionId
     * @return
     */
    @HandleDataException
    public boolean deleteCollectionSection(@Min(0) long sectionId) {
        return PersistenceUtils.delete(sectionId, collectionSectionMapper::deleteById);
    }
    //endregion
}
